<template>
  <component
    :is="isCreate ? AModal : ADrawer"
    v-model:visible="modalVisible"
    class="sl-dialog sl-drawer sl-inside-drawer"
    :title="modalTitle"
    :mask="isCreate"
    :class="isCreate ? (fieldsNum > 4 ? '!w-220' : '!w-168') : [modalVisible ? 'w-auto md:!w-168 p-3' : 'py-3']"
    :get-container="isCreate ? 'body' : drawerContainerSelector"
    :style="isCreate ? {} : { position: 'absolute' }"
    :content-wrapper-style="{ position: 'static', borderRadius: '4px', overflow: 'hidden' }"
    :body-style="{ paddingBottom: 0 }"
  >
    <div ref="formContainer" class="relative">
      <a-spin :tip="loadingText" :spinning="!!loadingText">
        <DynamicForm
          ref="dyForm"
          :columns="columns"
          :model="model"
          :model-date-fields="modelDateFields"
          :model-form-setting="modelFormSetting"
          :model-rules="modelRules"
          :on-before-reset-fields="onBeforeResetFields"
          :id-key="idKey"
          :cur-action="curAction"
          :disabled="disabledEdit"
          @before-upload="uploading = true"
          @upload="uploading = false"
        >
        </DynamicForm>
      </a-spin>
    </div>
    <template #footer>
      <div v-if="isCreate">
        <a-button :disabled="!!loadingText" class="!text-xs sm:!text-base" @click="hideModal">
          {{ t("common.cancel") }}
        </a-button>
        <a-button
          :disabled="!!loadingText || uploading"
          :type="hideNextAddBtn ? 'primary' : 'default'"
          class="!text-xs sm:!text-base"
          @click="modalOk()"
        >
          {{ t("common.ok") }}
        </a-button>
        <a-button
          v-if="!hideNextAddBtn"
          :disabled="!!loadingText || uploading"
          class="!text-xs sm:!text-base"
          type="primary"
          @click="modalOk(true)"
        >
          {{ t("common.okAndAdd") }}
        </a-button>
      </div>
      <div v-else class="flex flex-row items-center">
        <div class="flex-1 -ml-4.5">
          <a-button
            v-show="!hideNextPrev"
            type="text"
            :disabled="curUpdateIndex === 0 || !!loadingText || uploading"
            @click="updateNextOrPrev(-1)"
          >
            <LeftOutlined class="!align-middle h-4" />
            <span class="align-middle">{{ t("common.prev") }}</span>
          </a-button>
          <a-button
            v-show="!hideNextPrev"
            type="text"
            :disabled="curUpdateIndex === curRecords.length - 1 || !!loadingText || uploading"
            @click="updateNextOrPrev(1)"
          >
            <span class="align-middle">{{ t("common.next") }}</span>
            <RightOutlined class="!align-middle h-4" />
          </a-button>
        </div>
        <a-space v-if="isUpdate" :size="16">
          <a-button class="!text-xs sm:!text-base" :disabled="!!loadingText" @click="cancel">
            {{ t("common.cancel") }}
          </a-button>
          <a-button
            type="primary"
            class="!text-xs sm:!text-base"
            :disabled="!!loadingText || uploading || disabledEdit"
            @click="modalOk()"
          >
            {{ t("common.ok") }}
          </a-button>
        </a-space>
        <a-space v-else-if="isCheck && innerOperData && innerOperData.length > 0" :size="16">
          <template v-for="(el, i) in innerOperData">
            <slot v-if="el.customSlot" :name="el.customSlot" />
            <a-button
              v-else
              :key="i"
              :disabled="!!loadingText || uploading"
              :type="typeof el.type == 'function' ? el.type(model) : el.type || 'default'"
              :danger="typeof el.danger == 'function' ? el.danger(model) : el.danger"
              @click="el.handler(curRecord)"
            >
              {{ typeof el.label == "function" ? el.label(model) : el.label }}
            </a-button>
            <!-- <a-button
              v-else
              :key="i"
              :disabled="!!loadingText || uploading"
              class="!text-xs sm:!text-base"
              :type="typeof el.type == 'function' ? el.type(model) : el.type || 'default'"
              :danger="typeof el.danger == 'function' ? el.danger(model) : el.danger"
              @click="el.handler(curRecord)"
            >
              {{ typeof el.label == "function" ? el.label(model) : el.label }}
            </a-button> -->
          </template>
        </a-space>
      </div>
    </template>
  </component>
</template>
<script lang="ts" setup>
import { Modal as AModal, Drawer as ADrawer, message } from "ant-design-vue";
import type { TableColumnProps } from "ant-design-vue";
import { LeftOutlined, RightOutlined } from "@ant-design/icons-vue";
import { computed, nextTick, Ref, ref, shallowRef, watch } from "vue";
import { useI18n } from "/@/locales/useI18n";
import dayjs from "dayjs";
import InfoModal from "/@/components/InfoModal";
import { coverObj, isEmptyObj } from "/@/utils/common";
import CURD from "/@/api/curd";
import type { DeleteParams, ID, PaginationParam, SearchModel, TableRecord } from "/@/api/models";
import { findItemByKey } from "/@/utils/treeData";
import DynamicForm from "./DynamicForm.vue";
import { CURDAction, FormItemComponentType, isSelectComponent } from "./models";
import type {
  CURDDialogExpose,
  CURDModalEvent,
  FormItemData,
  OperItem,
  ShowCURDModalOptions,
  DynamicFormExpose,
  ModelRules,
} from "./models";
import { useHomeStore } from "/@/stores/home";

//现在还不支持复杂的类型和从其它文件进行类型导入。理论上来说，将来是可能实现类型导入的。
const props = withDefaults(
  defineProps<{
    createTitle?: string;
    updateTitle?: string;
    checkTitle?: string;
    deleteMessage?: string;
    deleteTitle?: string;
    columns?: TableColumnProps<TableRecord>[];
    operData?: OperItem<any>[];
    drawerContainerSelector?: string;
    idKey?: string;
    //通过设置该属性值打开窗口
    showOptions?: ShowCURDModalOptions;
    //form绑定的数据对象
    model: { [key: string]: any };
    //model里面所有日期字段名
    modelDateFields?: string[];
    //配置如何根据model展示form item，如果不配置则默认展示input text
    modelFormSetting?: FormItemData[];
    //数据校验规则 若为字符串字段key则说明该字段是必填项 否则是具体的校验规则
    modelRules?: ModelRules;
    tableData?: TableRecord[];
    curd?: CURD;
    reloadTable?: (params?: SearchModel | PaginationParam<SearchModel>) => void;
    //获取更新或删除的记录id值和id字段名，用于多表联合的表格
    getId?: (record: TableRecord) => { key: string; value: ID };
    onBeforeResetFields?: (data: FormItemData[]) => TableRecord;
    deleteUrlSuffixMode?: number;
    cannotEditIds?: string[];
    hideNextAddBtn?: boolean;
    getDetail?: (record: TableRecord) => Promise<TableRecord | null | undefined>;
  }>(),
  {
    drawerContainerSelector: "#home-content",
    idKey: "id",
    deleteUrlSuffixMode: 1,
  }
);
const emits = defineEmits<{
  (event: "open", e: CURDModalEvent): void;
  (event: "next", e: CURDModalEvent): void;
  (event: "prev", e: CURDModalEvent): void;
  (event: "beforeOk", e: CURDModalEvent): void;
  (event: "beforeUpdateModel", updateModel: TableRecord): void;
  (event: "ok", e: CURDModalEvent): void;
}>();
const { t } = useI18n();
const curAction = ref<CURDAction>(CURDAction.CREATE);
const dyForm = ref<DynamicFormExpose>();
//字段数量
let fieldsNum = 0;
for (let i in props.model) {
  fieldsNum++;
}
const uploading = ref(false);
//modal
const modalVisible = ref(false);
const homeStore = useHomeStore();
watch(modalVisible, (v) => {
  //控制页面内容是否能滚动
  if (isCheck.value || isUpdate.value) {
    homeStore.contentScrollable = !v;
  }
  if (!v) {
    //重置表单字段值
    dyForm.value?.resetFields();
  }
});
const modalTitle = ref("");
const loadingText = ref("");
//当前要更新或查看的记录索引
const curUpdateIndex = ref(0);
//当前要更新或查看的记录所在层的数据 如果不是树数据，则该数据跟props.tableData一致
const curRecords = ref<TableRecord[]>([]);
//当前要更新或查看的记录
const curRecord: Ref<TableRecord | null> = shallowRef(null);
const hideNextPrev = ref(false);
//某些记录不能编辑
const disabledEdit = ref(false);
//showOptions控制窗口显示
watch(
  () => props.showOptions,
  (options) => {
    if (!options) {
      hideModal();
      return;
    }
    if (options.action === CURDAction.DELETE) {
      deleteRecord(options);
    } else {
      showModal(options);
    }
  }
);
//表格数据变化时 同步更新当前层相关数据
if (props.tableData) {
  watch(() => props.tableData as TableRecord[], updateCurData);
}
function updateCurData(data: TableRecord[]) {
  if (!curRecord.value) return;
  const result = findItemByKey<TableRecord>(data, curRecord.value[props.idKey], props.idKey);
  if (result) {
    curUpdateIndex.value = result.index;
    curRecords.value = result.items;
  }
}
function hideModal() {
  modalVisible.value = false;
}
function cancel() {
  if (lastIsCheckWhenToUpdate) {
    //如果是由查看模式进入编辑模式 则点击取消的时候返回到查看模式
    showModal(lastCheckShowOptions);
  } else {
    hideModal();
  }
}
const isCreate = computed(() => curAction.value === CURDAction.CREATE);
const isUpdate = computed(() => curAction.value === CURDAction.UPDATE);
const isCheck = computed(() => curAction.value === CURDAction.CHECK);
//是否由查看模式进入编辑模式
let lastIsCheckWhenToUpdate = false;
//是否第一次打开modal
let isFirstShow = true;
let lastCheckShowOptions: ShowCURDModalOptions;
async function showModal(showOptions: ShowCURDModalOptions) {
  disabledEdit.value = false;
  lastIsCheckWhenToUpdate =
    modalVisible.value && curAction.value === CURDAction.CHECK && showOptions.action === CURDAction.UPDATE;
  //当窗口component发生变化时，需要先nextTick再进行后续操作
  const shouldWaitInit =
    (curAction.value === CURDAction.CREATE && showOptions.action !== CURDAction.CREATE) ||
    (curAction.value !== CURDAction.CREATE && showOptions.action === CURDAction.CREATE);
  curAction.value = showOptions.action;
  if (showOptions.action === CURDAction.CHECK) {
    lastCheckShowOptions = showOptions;
  }
  if (showOptions.title) {
    modalTitle.value = showOptions.title;
  } else {
    if (curAction.value === CURDAction.CREATE) {
      modalTitle.value = props.createTitle as string;
    } else if (curAction.value === CURDAction.UPDATE) {
      modalTitle.value = props.updateTitle as string;
    } else {
      modalTitle.value = props.checkTitle as string;
    }
  }
  hideNextPrev.value = !!showOptions.hideNextPrev;
  //显示窗口
  modalVisible.value = true;
  if (isFirstShow || shouldWaitInit) {
    //等待dyForm初始化完毕
    await nextTick();
  }
  if (!dyForm.value) return;
  if (isFirstShow || showOptions.isReloadData) {
    isFirstShow = false;
    //初始化异步数据
    await dyForm.value.initFormItemComponentData();
    // dyForm.value.initFormItemComponentData().then(() => {
    //   setSelectFormItemText(showOptions.record);
    // });
  } else {
    // setSelectFormItemText(showOptions.record);
  }
  dyForm.value?.resetFields();
  //触发open事件
  emits("open", {
    action: curAction.value,
    formItemData: dyForm.value.getFormItemData(),
    record: showOptions.record,
  });
  if (curAction.value !== CURDAction.CREATE) {
    let record = showOptions.record;
    if (record) {
      updateModelByRecord(record);
      props.tableData && updateCurData(props.tableData);
    }
  }
}

//查看模式下 设置select组件显示正确的text
function setSelectFormItemText(record) {
  if (curAction.value === CURDAction.CHECK && dyForm.value && record) {
    const formItemData = dyForm.value.getFormItemData();
    formItemData.forEach((el) => {
      if (el.textKey && el.props && isSelectComponent(el.type as FormItemComponentType)) {
        const data = el.props[el.type === FormItemComponentType.TREESELECT ? "treeData" : "options"];
        if (data) {
          const info = findItemByKey(data, record[el.key], el.valueKey || el.key);
          if (info) {
            el.text = info.item[el.textKey];
          } else {
            el.text = "--";
          }
        }
      }
    });
  }
}

async function updateModelByRecord(record: TableRecord) {
  curRecord.value = record;
  const copyRecord = { ...record };
  if ((props.curd && props.curd.getDetail) || props.getDetail) {
    loadingText.value = t("common.loadingDetail");
    let detail: any = {};
    if (props.getDetail) {
      detail = await props.getDetail(record);
    } else if (props.curd && props.curd.getDetail) {
      //目前getDetail只支持根据对应主键id获取，即参数是{[idKey]: idValue}
      detail = await props.curd.getDetail({ [props.idKey]: record[props.idKey] });
    }
    if (detail) {
      Object.assign(copyRecord, detail);
    }
    loadingText.value = "";
  }
  for (const key in copyRecord) {
    if (isEmptyObj(copyRecord[key])) {
      //{} => null
      copyRecord[key] = null;
    }
  }
  if (curAction.value === CURDAction.UPDATE) {
    disabledEdit.value = !!props.cannotEditIds && props.cannotEditIds.includes(copyRecord[props.idKey]);
    props.modelDateFields?.forEach((key) => {
      const value = copyRecord[key];
      if (value && typeof value == "string") {
        copyRecord[key] = dayjs(value);
      }
    });
  }
  const formItemData = dyForm.value?.getFormItemData() || [];
  formItemData.forEach((el) => {
    if (el.interValueToFormValue) {
      copyRecord[el.key] = el.interValueToFormValue(copyRecord[el.key], copyRecord, el);
    }
  });
  //在更新前触发一次beforeUpdateModel事件，让使用者可对copyRecord进行修改
  emits("beforeUpdateModel", copyRecord);
  coverObj(props.model, copyRecord);
  setSelectFormItemText(copyRecord);
}

function updateNextOrPrev(d: number) {
  const targetIndex = curUpdateIndex.value + d;
  if (targetIndex < 0 || targetIndex >= curRecords.value.length) {
    return;
  }
  const record = curRecords.value[targetIndex];
  if (lastCheckShowOptions) {
    //这里记得同步更新 最近一次 查看模式下的配置数据
    lastCheckShowOptions.record = record;
  }
  updateModelByRecord(record);
  curUpdateIndex.value = targetIndex;
  if (dyForm.value) {
    const e = {
      action: curAction.value,
      formItemData: dyForm.value.getFormItemData(),
      record,
    };
    d > 0 ? emits("next", e) : emits("prev", e);
  }
}

function modalOk(isContinue?: boolean) {
  dyForm.value?.validate().then(async () => {
    const model = { ...props.model };
    if (curAction.value === CURDAction.UPDATE && curRecord.value) {
      if (props.getId) {
        const idKv = props.getId(curRecord.value);
        model[idKv.key] = idKv.value;
      } else {
        model[props.idKey] = curRecord.value[props.idKey];
      }
    }
    props.modelDateFields?.forEach((field) => {
      const value = model[field];
      if (value && value instanceof dayjs) {
        model[field] = (value as dayjs.Dayjs).format(t("common.dateTimeFormatStr"));
      }
    });
    const formItemData = dyForm.value?.getFormItemData() || [];
    formItemData.forEach((el) => {
      if (el.formValueToInterValue) {
        model[el.key] = el.formValueToInterValue(model[el.key], model, el);
      }
    });
    emits("beforeOk", {
      model,
      action: curAction.value,
      formItemData,
    });
    if (props.curd) {
      let action;
      if (props.curd.update && curAction.value === CURDAction.UPDATE) {
        action = props.curd.update.bind(props.curd);
      } else if (props.curd.create && curAction.value === CURDAction.CREATE) {
        action = props.curd.create.bind(props.curd);
      }
      if (action) {
        const result = await action(model);
        if (result !== null) {
          message.success(t("common.operSuccess"));
          emits("ok", {
            model,
            action: curAction.value,
            formItemData,
          });
          if (isContinue) {
            dyForm.value?.resetFields();
          } else {
            if (curAction.value === CURDAction.CREATE || hideNextPrev.value) {
              modalVisible.value = false;
            } else {
              cancel();
            }
          }
          if (props.reloadTable) {
            await props.reloadTable();
            if (lastCheckShowOptions && props.tableData) {
              updateCurData(props.tableData);
              lastCheckShowOptions.record = curRecords.value[curUpdateIndex.value];
            }
          }
        }
      }
    }
  });
}
function deleteRecord(options: ShowCURDModalOptions) {
  InfoModal.warningConfirm({
    content: options.message || props.deleteMessage,
    title: options.title || props.deleteTitle || t("common.tip"),
    okText: t("common.delete"),
    onOk: async (close: any) => {
      if (!props.curd || !props.curd.delete) return;
      let params: DeleteParams = {
        idKey: props.idKey,
      };
      let ids = options.ids;
      if (ids) {
        //批量删除
        params.idValue = ids;
      } else {
        let record = options.record;
        if (!record) {
          if (!curRecord.value) return;
          record = curRecord.value;
        }
        const id = props.getId ? props.getId(record).value : (record[props.idKey] as string);
        params.idValue = id;
        if (props.deleteUrlSuffixMode) {
          params.urlSuffix = props.deleteUrlSuffixMode === 1 ? `?id=${id}` : `${id}`;
        }
      }
      const result = await props.curd.delete(params);
      close();
      if (result) {
        emits("ok", {
          action: CURDAction.DELETE,
          formItemData: [],
        });
        message.success(t("common.operSuccess"));
        if (props.reloadTable) {
          await props.reloadTable();
        }
        modalVisible.value = false;
      }
    },
  });
}
const innerOperData = computed(() =>
  props.operData
    ? props.operData.filter((el) => {
        if (el.showInDialog === false) return false;
        let label = el.label;
        if (typeof label == "function") {
          label = label(props.model);
        }
        return !!label;
      })
    : []
);
const formContainer = ref<HTMLDivElement>();
const myExpose: CURDDialogExpose = {
  isOpen() {
    return modalVisible.value;
  },
  getOptionItemByValue(formItemKey, optionValue) {
    return dyForm.value?.getOptionItemByValue(formItemKey, optionValue);
  },
  reloadFormItemComponentData(formItemKey, params) {
    dyForm.value?.reloadFormItemComponentData(formItemKey, params);
  },
  setFieldRquired(key, required) {
    dyForm.value?.setFieldRquired(key, required);
  },
  toggleField(key, hide) {
    dyForm.value?.toggleField(key, hide);
  },
  getFormContainer() {
    return formContainer.value;
  },
};
defineExpose(myExpose);
</script>
<style lang="less">
.check-detail-form {
  .ant-form-item-label > label {
    color: @color-secondary;
  }
}
</style>
